﻿#include "pch.h"
#include "sbcsp.h"
#include "sbctn.h"
#include "kstub_mutex.h"

#if _SB_PTHREAD_ 
#if _SB_WINDOWS_
#include <WinSock2.h>
#include "posix/semaphore.h"
#else
#include <semaphore.h>
#endif

//////////////////////////////////////////////////////////////////////////
// 스레드 정의
struct kThread
{
	kcham				canwait;
	kint				busytype;
	kint				stacksize;

	kanycb				cb;
	kpointer			ret;

	pthread_t			pth;

	kpointer			tls[64];

	kcham				isreg;
	kThread*			next;
};


//////////////////////////////////////////////////////////////////////////
// 스레드 구현
static struct kImplThd
{
	int					inittmp;

	pthread_key_t		selftls;
	kuint				tlsindex;
	kNotifyFunc			tlsdispose[64];

	kThread*			selfthd;
	kThread*			thdlist;

	kint				maxstack;
	kint				prority[6];

	pthread_t			nullpth;
} k_impl_thd =
{
	0,

	0,
	0,
	{NULL, },

	NULL,
	NULL,

	0,
	{0, },

	0,
};

static void _k_thd_finalize(kThread* self, kuint tlscnt, kcham force);

static kcham _k_thd_is_null(pthread_t* pth)
{
	return memcmp(pth, &k_impl_thd.nullpth, sizeof(pthread_t)) == 0;
}

static void _k_thd_make_null(pthread_t* pth)
{
	memcpy(pth, &k_impl_thd.nullpth, sizeof(pthread_t));
}

static void _k_thd_key_disp(kpointer ptr)
{
}

// 스레드 초기화
void _k_thd_init(void)
{
	struct sched_param sp;
	int policy;

#ifdef _SC_THREAD_STACK_MIN
	k_impl_thd.maxstack = K_MAX(sysconf(_SC_THREAD_STACK_MIN), 0);
#endif

	pthread_getschedparam(pthread_self(), &policy, &sp);

	k_impl_thd.prority[0] = 0;
	k_impl_thd.prority[1] = 0;
	k_impl_thd.prority[2] = sp.sched_priority;
	k_impl_thd.prority[3] = sp.sched_priority;
#ifdef _SB_BSD_
	k_impl_thd.prority[4] = 31;
#else
	k_impl_thd.prority[5] = sp.sched_priority;
#endif

	pthread_key_create(&k_impl_thd.selftls, _k_thd_key_disp);
	k_impl_thd.selfthd = k_thread_self();
}

// 스레드 제거
void _k_thd_disp(void)
{
	kThread* node;
	kThread* next;

	if (k_impl_thd.selftls != 0)
	{
		pthread_key_delete(k_impl_thd.selftls);
		k_impl_thd.selftls = 0;
	}

	for (node = k_impl_thd.thdlist; node; node = next)
	{
		next = node->next;
		_k_thd_finalize(node, k_impl_thd.tlsindex, TRUE);
	}
}

static kint _k_thd_get_busy(kThread* self)
{
	struct sched_param sp;
	int i, policy;

	pthread_getschedparam(self->pth, &policy, &sp);

	for (i = 0; i < 6; i++)
	{
		if (sp.sched_priority == k_impl_thd.prority[i])
			return i - 2;
	}

	return 0;
}

static kcham _k_thd_set_busy(kThread* self, kint busytype)
{
	struct sched_param sp;
	int policy;

	pthread_getschedparam(self->pth, &policy, &sp);
	sp.sched_priority = k_impl_thd.prority[K_CLAMP(busytype + 2, 0, 5)];
	pthread_setschedparam(self->pth, policy, &sp);

	return TRUE;
}

static void _k_thd_finalize(kThread* self, kuint tlscnt, kcham force)
{
	kuint i;

	for (i = 0; i < tlscnt; i++)
	{
		kNotifyFunc func = k_impl_thd.tlsdispose[i];
		kpointer value = self->tls[i];

		if (func && value)
			func(value);
	}

	if (self->canwait)
	{
		if (force)
			k_delete(self);
	}
	else
	{
		if (!_k_thd_is_null(&self->pth))
			_k_thd_make_null(&self->pth);

		// 대기 안하는 애들은 강제 지움
		k_delete(self);
	}
}

static void _k_thd_exit(kThread* self, kcham isexit)
{
	kThread* node;
	kThread* prev;
	int tlscnt;

	if (self->isreg)
	{
		self->isreg = FALSE;
		kp_lock();

		for (node = k_impl_thd.thdlist, prev = NULL; node; prev = node, node = node->next)
		{
			if (node == self)
			{
				if (prev)
					prev->next = node->next;
				else
					k_impl_thd.thdlist = node->next;

				break;
			}
		}

		kp_unlock();
	}

	tlscnt = k_impl_thd.tlsindex;
	_k_thd_finalize(self, tlscnt, FALSE);

	pthread_setspecific(k_impl_thd.selftls, NULL);

	if (isexit)
		pthread_exit(NULL);
}


//////////////////////////////////////////////////////////////////////////
// tls

// 만들기
kTls* k_tls(kNotifyFunc func)
{
	kTls* self;

	kp_lock();

	if (k_impl_thd.tlsindex >= 64)
	{
		kp_unlock();

		k_tracef(KLV_FATAL, "hb.tls", "too many tls used. limited value is %d.", 64);
		return NULL;
	}

	self = (kTls*)K_CAST_UINT_TO_PTR(k_impl_thd.tlsindex + 0x10);
	k_impl_thd.tlsdispose[k_impl_thd.tlsindex] = func;
	k_impl_thd.tlsindex++;

	kp_unlock();

	return self;
}

// 넣기
void k_settls(kTls* self, kpointer value)
{
	kThread* thd;
	kuint at = K_CAST_PTR_TO_UINT(self) - 0x10;

	k_return_if_fail(at < 64);

	thd = k_thread_self();
	thd->tls[at] = value;
}

// 얻기
kpointer k_gettls(kTls* self)
{
	kThread* thd = k_thread_try();
	kuint at = K_CAST_PTR_TO_UINT(self) - 0x10;

	return (at >= 64 || !thd) ? NULL : thd->tls[at];
}


//////////////////////////////////////////////////////////////////////////
// 스레드

kThread* k_thread_try(void)
{
	kThread* self = (kThread*)pthread_getspecific(k_impl_thd.selftls);
	return self;
}

kThread* k_thread_self(void)
{
	kThread* self = (kThread*)pthread_getspecific(k_impl_thd.selftls);

	if (!self)
	{
		self = k_new_1(kThread);

		self->pth = pthread_self();

		self->canwait = FALSE;
		self->busytype = _k_thd_get_busy(self);
		self->stacksize = 0;

		self->cb.func = NULL;
		self->cb.data = NULL;

		k_zero(self->tls, 64, kpointer);

		self->isreg = TRUE;

		kp_lock();
		self->next = k_impl_thd.thdlist;
		k_impl_thd.thdlist = self;
		kp_unlock();

		pthread_setspecific(k_impl_thd.selftls, self);
	}

	return self;
}

// 만들기
kThread* k_thread_new(kThreadFunc func, kpointer userdata, kuint stacksize, kuint busytype)
{
	kThread* self = k_new_1(kThread);

	self->canwait = TRUE;
	self->busytype = busytype;
	self->stacksize = stacksize;
	self->cb.func = (kfunc)func;
	self->cb.data = userdata;

	self->ret = NULL;

	_k_thd_make_null(&self->pth);
	k_zero(self->tls, 64, kpointer);

	self->isreg = TRUE;

	kp_lock();
	self->next = k_impl_thd.thdlist;
	k_impl_thd.thdlist = self;
	kp_unlock();

	return self;
}

// 만들기
kcham k_thread_run(kThreadFunc func, kpointer userdata, kuint stacksize, kuint busytype)
{
	kThread* self = k_new_1(kThread);

	self->canwait = FALSE;
	self->busytype = busytype;
	self->stacksize = stacksize;
	self->cb.func = (kfunc)func;
	self->cb.data = userdata;

	self->ret = NULL;

	_k_thd_make_null(&self->pth);
	k_zero(self->tls, 64, kpointer);

	self->isreg = TRUE;

	kp_lock();
	self->next = k_impl_thd.thdlist;
	k_impl_thd.thdlist = self;
	kp_unlock();

	return k_thread_start(self);
}

// 제거
void k_thread_delete(kThread* self)
{
	kThread* node;
	kThread* prev;

	k_return_if_fail(self->canwait != FALSE);

	k_thread_wait(self);

	if (self->isreg)
	{
		self->isreg = FALSE;
		kp_lock();

		for (node = k_impl_thd.thdlist, prev = NULL; node; prev = node, node = node->next)
		{
			if (node == self)
			{
				if (prev)
					prev->next = node->next;
				else
					k_impl_thd.thdlist = node->next;

				break;
			}
		}

		kp_unlock();
	}

	k_delete(self);
}

// 스레드 시작 지점
static kpointer _k_thd_entry(kpointer ptr)
{
	kThread* self = (kThread*)ptr;

	pthread_setspecific(k_impl_thd.selftls, self);

	kp_lock();
	kp_unlock();

	self->ret = ((kThreadFunc)self->cb.func)(self->cb.data);

	_k_thd_exit(self, TRUE);

	return 0;
}

// 시작
kcham k_thread_start(kThread* self)
{
	pthread_attr_t attr;
	struct sched_param sp;
	int res, stk;

	if (!_k_thd_is_null(&self->pth))
		return FALSE;

	pthread_attr_init(&attr);

	// 스택
	if (self->stacksize <= 0)
		stk = 0;
	else
	{
		stk = K_MAX(k_impl_thd.maxstack, self->stacksize);
		pthread_attr_setstacksize(&attr, stk);
	}

	// 조인
	pthread_attr_setdetachstate(&attr, self->canwait ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);

	// 바쁨
	if (self->busytype != 0)
	{
		pthread_attr_getschedparam(&attr, &sp);
		sp.sched_priority = k_impl_thd.prority[K_CLAMP(self->busytype + 2, 0, 5)];
		pthread_attr_setschedparam(&attr, &sp);
	}

	// 고고
	kp_lock();
	res = pthread_create(&self->pth, &attr, _k_thd_entry, self);
	kp_unlock();

	//
	pthread_attr_destroy(&attr);

	//return res!=EAGAIN;
	return res == 0;
}

// 기둘
kpointer k_thread_wait(kThread* self)
{
	kpointer ret;

	k_return_value_if_fail(self->canwait != FALSE, NULL);

	if (_k_thd_is_null(&self->pth))
	{
		// 스레드가 널
		ret = NULL;
	}
	else
	{
		pthread_join(self->pth, &ret);
		_k_thd_make_null(&self->pth);
		ret = self->ret;
	}

	return ret;
}

// 나가기
void k_thread_exit(kpointer ret)
{
	kThread* self = k_thread_self();

	self->ret = ret;

	_k_thd_exit(self, TRUE);
}

// 바쁨
kcham k_thread_set_busy(kThread* self, kint busytype)
{
	if (_k_thd_is_null(&self->pth))
	{
		self->busytype = busytype;
		return TRUE;
	}
	else
	{
		if (!_k_thd_set_busy(self, busytype))
			return FALSE;
		else
		{
			self->busytype = busytype;
			return TRUE;
		}
	}
}

// 스택 크기
kcham k_thread_set_stack(kThread* self, kuint stacksize)
{
	if (_k_thd_is_null(&self->pth))
		return FALSE;
	else
	{
		self->stacksize = stacksize;
		return TRUE;
	}
}

// 콜백
kcham k_thread_set_cb(kThread* self, kThreadFunc func, kpointer data)
{
	if (_k_thd_is_null(&self->pth))
		return FALSE;
	else
	{
		self->cb.func = (kfunc)func;
		self->cb.data = data;
		return TRUE;
	}
}

// 우선 순위
kint k_thread_get_busy(kThread* self)
{
	return self->busytype;
}

// 스택
kuint k_thread_get_stack(kThread* self)
{
	return self->stacksize;
}

// 기둘
kcham k_thread_can_wait(kThread* self)
{
	return self->canwait;
}

// 아이디
kuint k_thread_get_id(kThread* self)
{
	return K_CAST_PTR_TO_UINT(self);
}


//////////////////////////////////////////////////////////////////////////
// 뮤텍스
struct kMutexFuncTable
{
	kfunc destroy;
	kfunc lock;
	kfunc unlock;
};

struct kMutexFuncTable k_ft_mutex =
{
	(kfunc)pthread_mutex_destroy,
	(kfunc)pthread_mutex_lock,
	(kfunc)pthread_mutex_unlock
};

struct kMutexFuncTable k_ft_spin =
{
#if !_SB_ANDROID_
	(kfunc)pthread_spin_destroy,
	(kfunc)pthread_spin_lock,
	(kfunc)pthread_spin_unlock
#else
	(kfunc)pthread_mutex_destroy,
	(kfunc)pthread_mutex_lock,
	(kfunc)pthread_mutex_unlock
#endif
};

struct kMutex
{
	union
	{
		pthread_mutex_t     mutex;
#if !_SB_ANDROID_
		pthread_spinlock_t	spin;
#else
		pthread_mutex_t		spin;
#endif
	};
	struct kMutexFuncTable*	pft;
	kcham					isspin;
};

kMutex* k_mutex_new(void)
{
	kMutex* self = k_new_1(kMutex);
	pthread_mutex_init(&self->mutex, k_mutexattr());
	self->pft = &k_ft_mutex;
	self->isspin = FALSE;
	return self;
}

kMutex* k_mutex_new_count(kint count)
{
	kMutex* self = k_new_1(kMutex);
#if !_SB_ANDROID_
	pthread_spin_init(&self->spin, count);
#else
	pthread_mutex_init(&self->spin, k_mutexattr());
#endif
	self->pft = &k_ft_spin;
	self->isspin = TRUE;
	return self;
}

void k_mutex_delete(kMutex* self)
{
	self->pft->destroy((void*)&self->mutex);
	k_delete(self);
}

void k_mutex_enter(kMutex* self)
{
	self->pft->lock((void*)&self->mutex);
}

void k_mutex_leave(kMutex* self)
{
	self->pft->unlock((void*)&self->mutex);
}


//////////////////////////////////////////////////////////////////////////
// 컨디션
struct kCond
{
	pthread_cond_t      cond;
};

kCond* k_cond_new(void)
{
	kCond* self = k_new_1(kCond);
	pthread_cond_init(&self->cond, k_condattr());
	return self;
}

void k_cond_delete(kCond* self)
{
	pthread_cond_destroy(&self->cond);
	k_delete(self);
}

void k_cond_signal(kCond* self)
{
	pthread_cond_signal(&self->cond);
}

void k_cond_broadcast(kCond* self)
{
	pthread_cond_broadcast(&self->cond);
}

kcham k_cond_wait_for(kCond* self, kMutex* enterlock, kint milliseconds)
{
	struct timeval tv;
	struct timespec sp;
	kcham timeout;
	int res;

	k_return_value_if_fail(enterlock != NULL, FALSE);
	k_return_value_if_fail(!enterlock->isspin, FALSE);

	if (milliseconds < 0)
	{
		res = pthread_cond_wait(&self->cond, &enterlock->mutex);
		timeout = FALSE;
	}
	else
	{
		k_timeval_now(&tv);
		k_timeval_add_milli(&tv, milliseconds);

		sp.tv_sec = tv.tv_sec;
		sp.tv_nsec = tv.tv_usec * (K_CONST_NSEC_PER_SEC / K_CONST_USEC_PER_SEC);

		k_return_value_if_fail(sp.tv_nsec < K_CONST_NSEC_PER_SEC, TRUE);

		res = pthread_cond_timedwait(&self->cond, &enterlock->mutex, &sp);
		timeout = res == ETIMEDOUT;
	}

	if (!timeout)
	{
		if (res)
			k_tracef(KLV_WARNING, "hb.cond", "failed! (code: %d)", res);
	}

	return !timeout;
}

void k_cond_wait(kCond* self, kMutex* enterlock)
{
	k_cond_wait_for(self, enterlock, -1);
}


//////////////////////////////////////////////////////////////////////////
// 세마포어
struct kSem
{
	sem_t				s;
};

kSem* k_sem_new(kint initial)
{
	kSem* self = k_new_1(kSem);

	if (sem_init(&self->s, 0, initial) < 0)
	{
		k_traces(KLV_ERROR, "k.sem", "fail to create semaophore");
		k_delete(self);
		self = NULL;
	}

	return self;
}

void k_sem_delete(kSem* self)
{
	sem_destroy(&self->s);

	k_delete(self);
}

kcham k_sem_wait(kSem* self)
{
	if (sem_wait(&self->s) < 0)
		return FALSE;
	else
		return TRUE;
}

kcham k_sem_try_wait(kSem* self)
{
	if (sem_trywait(&self->s) == 0)
		return TRUE;
	else
		return FALSE;
}

kcham k_sem_wait_for(kSem* self, kint milliseconds)
{
	struct timeval tv;
	struct timespec sp;
	int res;

	if (milliseconds == 0)
		return k_sem_try_wait(self);

	if (milliseconds < 0)
		return k_sem_wait(self);

	k_timeval_now(&tv);
	k_timeval_add_milli(&tv, milliseconds);

	sp.tv_sec = tv.tv_sec;
	sp.tv_nsec = tv.tv_usec * (K_CONST_NSEC_PER_SEC / K_CONST_USEC_PER_SEC);

	do
	{
		res = sem_timedwait(&self->s, &sp);
	} while (res < 0 && errno == EINTR);

	if (res == 0)
		return TRUE;
	else
	{
		if (errno != ETIMEDOUT)
			k_traces(KLV_ERROR, "k.sem.waitfor", NULL);

		return FALSE;
	}
}

kint k_sem_value(kSem* self)
{
	kint res;

	sem_getvalue(&self->s, &res);

	return res < 0 ? 0 : res;
}

kcham k_sem_post(kSem* self)
{
	if (sem_post(&self->s) == 0)
		return TRUE;
	else
	{
		k_traces(KLV_ERROR, "k.sem.post", NULL);

		return FALSE;
	}
}

#endif

